#ifndef SIMPLE_SUPPORT_META_FLATTEN_HPP
#define SIMPLE_SUPPORT_META_FLATTEN_HPP

#include <type_traits> // std::enable_if_t std::is_same_v

#include "list.hpp" // list_constructor
#include "bind.hpp" // bind
#include "reconstruct.hpp" // reconstruct_t

namespace simple::support::meta
{
	template <typename ListCons, typename T, typename Enabled = void>
	struct flatten_once_operator
	{ using type = typename ListCons::template function<T>; };
	template <typename ListCons, template <typename...> class List, typename... Ts>
	struct flatten_once_operator<ListCons, List<Ts...>,
		std::enable_if_t<std::is_same_v<typename ListCons::template function<Ts...>, List<Ts...>>> >
	{ using type = List<Ts...>; };

	template <typename ListCons, typename T, typename Enabled = void>
	struct flatten_operator
	{ using type = typename ListCons::template function<T>; };
	template <typename ListCons, template <typename...> class List, typename... Ts>
	struct flatten_operator<ListCons, List<Ts...>,
		std::enable_if_t<std::is_same_v<typename ListCons::template function<Ts...>, List<Ts...>>> >
	{ using type = reconstruct_t<List<Ts...>,
		bind<flatten_operator, ListCons>::template bound>; };

	template <typename List> using flatten_once_t = reconstruct_t<List,
		bind<flatten_once_operator, list_constructor<List>>::template bound>;

	template <typename List> using flatten_t = reconstruct_t<List,
		bind<flatten_operator, list_constructor<List>>::template bound>;

} // namespace simple::support::meta

#endif /* end of include guard */
